﻿using System;
using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;

using C1.Xaml.Chart;
using SelfMeter.Data;
using System.Diagnostics;
using System.Collections.Generic;

namespace SelfMeter
{
    
    public sealed partial class DataChart : UserControl, ISelfMeterView
    {
        class MeterItemConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, string language)
            {
                MeterItem mi = value as MeterItem;
                bool? isDate = parameter as bool?;
                if (isDate != null && mi != null)
                {
                    if ((bool)isDate)
                        return mi.Date;//return mi.Date.ToString("d");
                    else
                        return mi.Data;
                }
                else
                {
                    throw new ArgumentException("Unexpected parameter type");
                }
            }

            public object ConvertBack(object value, Type targetType, object parameter, string language)
            {
                throw new NotImplementedException();
            }
        }

        ObservableCollection<MeterItem> _pts = null;
        int _counter = 0;
        DispatcherTimer _dt;
        int _maxPoints = 30;
        bool _timerPaused = true;
        const int _addPoints = 1;
        MeterDefinition _meter = null;
        ChartType[] _chartTypes = new ChartType[] { ChartType.LineSmoothed, ChartType.Bubble, ChartType.Column };
        int _currentStyle = 0;
        
        public DataChart(MeterDefinition md)
        {
            this.InitializeComponent();
            _meter = md;
            _pts = new ObservableCollection<MeterItem>();
            Debug.Assert(_meter.MeterData != null);

            chart.ChartType = _chartTypes[_currentStyle];

            // initialize axis scale and scrollbar
            chart.View.AxisX.ScrollBar = new AxisScrollBar();
            chart.View.AxisX.Title = "Date";
            chart.View.AxisX.AnnoAngle = -90;
            chart.View.AxisX.IsTime = true;
            chart.View.AxisX.AnnoFormat = "d";
            chart.View.AxisX.FlowDirection = Windows.UI.Xaml.FlowDirection.LeftToRight;
            chart.View.AxisY.Title = string.Format("{0}({1})", _meter.Name, _meter.Unit);
            //chart.View.AxisY.Min = 0;
            //chart.View.AxisY.Max = 100;

            // remove any action update delay
            chart.ActionUpdateDelay = 0;

            // set manipulation mode to translate X axis with inertia
            chart.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateInertia;

            // assign translate action to slide gesture
            chart.GestureSlide = GestureSlideAction.Translate;

            if (_meter.MeterData.Count > 0)
            {
                _counter = FillChart(_maxPoints, _meter.MeterData, _counter, false);
            }

            _dt = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(0.5) };
            _dt.Tick += (s, e) => Update();
            
            // _dt.Start();

        }

        private XYDataSeries NewDataSource()
        {
            return new XYDataSeries()
            {
                XValueBinding = new Binding() { Converter = new MeterItemConverter(), ConverterParameter = true },
                ValueBinding = new Binding() { Converter = new MeterItemConverter(), ConverterParameter = false },
                ConnectionStrokeThickness = 2,
                Label = _meter.Name,
                //    ConnectionStroke = new SolidColorBrush(Colors.DarkGray)
            };
        }


        void Update()
        {
            if (!_timerPaused && _meter.MeterData.Count > _maxPoints)
            {
                chart.BeginUpdate();

                int cnt = _addPoints;
                for (int i = 0; i < cnt; i++)
                {
                    _pts.Add(_meter.MeterData[_counter]);
                    _counter = (_counter + 1) % _meter.MeterData.Count;
                }

                int ndel = _pts.Count - _maxPoints;
                if (ndel > 0)
                    for (int i = 0; i < ndel; i++)
                        _pts.RemoveAt(0);

                chart.EndUpdate();
            }
        }

        private void OnTimerButtonClick(object sender, RoutedEventArgs e)
        {
            Button btn = (Button)sender;
           
            if (!_timerPaused)
            {
                _timerPaused = true;
                _dt.Stop();
                btn.Content = "Start";
                OnStopTimer();
            }
            else
            {
                OnStartTimer();
                _dt.Start();
                btn.Content = "Stop";
                _timerPaused = false;
            }         
        }

        private void OnStartTimer()
        {
            _counter = FillChart(_maxPoints, _meter.MeterData, _counter, true);
        }

        internal void OnStopTimer()
        {
            _counter = FillChart(_maxPoints, _meter.MeterData, _counter, false);
        }

        

        private int FillChart(int displayNum, List<MeterItem> mis, int start, bool move)
        {
            Debug.Assert(displayNum > 0);

            chart.BeginUpdate();
            start = UpdateDataAndView(displayNum, mis, start, move);

            chart.EndUpdate();
            return start;
        }

        private int UpdateDataAndView(int displayNum, List<MeterItem> mis, int start, bool move)
        {
            if (mis == null || mis.Count == 0)
                return start;

            if (chart.Data.Children.Count == 0)
            {
                chart.Data.Children.Add(NewDataSource());
            }

            chart.DataContext = null;
            
            int dataLength = mis.Count;
            _pts.Clear();

            start = displayNum < dataLength ? Math.Min(start, dataLength - displayNum) : 0;
            int min = move ? Math.Min(displayNum, dataLength) : dataLength;
            if (!move)
            {
                chart.View.AxisX.Scale = displayNum > dataLength ? 1.0 : (double)displayNum / (double)dataLength;
                chart.View.AxisX.Value = displayNum > dataLength ? 0.0 : (double)start / (double)dataLength;
            }
            else
            {
                chart.View.AxisX.Scale = 1.0;
               // chart.View.AxisX.Value = 0.0;

            }

            double maxV = 0;
            for (int i = 0; i < min; i++)
            {
                var itm = mis[start];
                if (itm.Data > maxV)
                    maxV = itm.Data;

                _pts.Add(itm);
                start = (start + 1) % dataLength;
            }

            chart.View.AxisY.Min = 0;
            chart.View.AxisY.Max = maxV + 5;
            chart.DataContext = _pts;

            return start;
        }

        private void OnScaleChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
            Slider s = sender as Slider;

            int oldvalue = _maxPoints; 
            _maxPoints = (int)s.Value;
            if (_meter != null && oldvalue != _maxPoints && _timerPaused)
            {
                _counter = FillChart(_maxPoints, _meter.MeterData, _counter, false);
            }
        }

        public void UpdateView(MeterDefinition meter, bool load)
        {
            _meter = meter;
            if (!load)
            {
                _timerPaused = true;
                _dt.Stop();
            }
            else
            {
                if (_meter.MeterData.Count > 0)
                {
                    _meter.MeterData.Sort((a, b) => { return a.Date.CompareTo(b.Date);  });

                    _counter = FillChart(_maxPoints, _meter.MeterData, _counter, false);
                }
                else
                {
                    _counter = 0;
                    _pts.Clear();
                }
            }
        }

        private void OnChangeStyle(object sender, RoutedEventArgs e)
        {
            _currentStyle = (_currentStyle + 1) % _chartTypes.Length;
            chart.ChartType = _chartTypes[_currentStyle];
            //FillChart(_maxPoints, _meter.MeterData, _counter, false);
        }
    }
}
